#include "util.h"
#include "posix_api.h"


int m_socket(__attribute__((unused)) int domain, int type, __attribute__((unused)) int protocol){
    int fd = get_fd_frombitmap();
    if(type == SOCK_DGRAM){
        struct local_host * host = rte_malloc("m socket host", sizeof(struct local_host), 0);
        if(host == NULL){
            return -1;
        }
        memset(host, 0, sizeof(struct local_host));
        host->fd = fd;
        host->protocol = IPPROTO_UDP;
        host->recv_buf = rte_ring_create("recv ring ", RING_SIZE, rte_socket_id(), RING_F_SP_ENQ|RING_F_SC_DEQ);
        if(host->recv_buf == NULL){
            rte_free(host);
            return -1;
        }
        host->send_buf = rte_ring_create("send ring ", RING_SIZE, rte_socket_id(), RING_F_SP_ENQ|RING_F_SC_DEQ);
        if(host->send_buf == NULL){
            rte_ring_free(host->recv_buf);
            rte_free(host);
            return -1;
        }

        pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
        rte_memcpy(&host->cond, &blank_cond, sizeof(pthread_cond_t));
        pthread_mutex_t  mtx = PTHREAD_MUTEX_INITIALIZER;
        rte_memcpy(&host->mtx, &mtx, sizeof(pthread_mutex_t));

        LL_ADD(host, g_hosts);
    }else if(type == SOCK_STREAM){
        struct tcp_stream * stream = rte_malloc("sock malloc tcp stream", sizeof(struct tcp_stream), 0);
        if (stream == NULL) {
            return -1;
        }
        /**
         *  此时 没有 ip 端口
         * */
        memset(stream, 0, sizeof(struct tcp_stream));
        stream->fd = fd;
        stream->protocol = IPPROTO_TCP;
        stream->next = stream->prev = NULL;
        stream->rcv_buf = rte_ring_create("socket tcp recv buffer", RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
        if (stream->rcv_buf == NULL) {
            rte_free(stream);
            return -1;
        }
        stream->send_buf = rte_ring_create("socket tcp send buffer", RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
        if (stream->send_buf == NULL) {
            rte_ring_free(stream->rcv_buf);
            rte_free(stream);
            return -1;
        }
        pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
        rte_memcpy(&stream->cond, &blank_cond, sizeof(pthread_cond_t));
        pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
        rte_memcpy(&stream->mtx, &blank_mutex, sizeof(pthread_mutex_t));
        struct tcp_table *table = get_tcp_instance();
        LL_ADD(stream, table->links);
    }
    return fd;
}


int m_bind(int socket_fd, const struct sockaddr * address, __attribute__((unused)) socklen_t len){
    void * host_info = get_host_info_from_fd(socket_fd);
    if(host_info == NULL){
        return -1;
    }
    struct local_host * host = (struct local_host *)host_info;
    if(host->protocol == IPPROTO_UDP){
        const struct sockaddr_in * server_address = (const struct sockaddr_in *)address;
        host->local_port = server_address->sin_port;
        rte_memcpy(&host->local_ip, &server_address->sin_addr.s_addr, sizeof(uint32_t));
        rte_memcpy(host->local_mac, g_src_mac, RTE_ETHER_ADDR_LEN);
    }else if(host->protocol == IPPROTO_TCP){
        struct tcp_stream *stream = (struct tcp_stream *)host_info;

        const struct sockaddr_in *laddr = (const struct sockaddr_in *)address;
        stream->d_port = laddr->sin_port;
        rte_memcpy(&stream->d_ip, &laddr->sin_addr.s_addr, sizeof(uint32_t));
        rte_memcpy(stream->local_mac, g_src_mac, RTE_ETHER_ADDR_LEN);

        stream->status = TCP_STATUS_CLOSED;
    }
    return  0;
}


int m_close(int fd){
    printf("---------m_close------------------>> \n");

    void * host_info = get_host_info_from_fd(fd);
    if(host_info == NULL){
        return  -1;
    }
    struct local_host * host = (struct local_host *)host_info;
    if(host->protocol == IPPROTO_UDP){
        LL_REMOVE(host, g_hosts);
        if(host->recv_buf){
            rte_ring_free(host->recv_buf);
        }
        if(host->send_buf){
            rte_ring_free(host->send_buf);
        }
        rte_free(host);
        set_fd_frombitmap(fd);
    }else if(host->protocol == IPPROTO_TCP){
        struct tcp_stream * stream = (struct tcp_stream *) host_info;
        if(stream->status != TCP_STATUS_LISTEN){
            struct tcp_fragment * fragment = rte_malloc("close malloc tcp fragment", sizeof(struct tcp_fragment), 0);
            if(fragment == NULL){
                return -1;
            }
            printf("close --> enter last ack\n");
            fragment->data = NULL;
            fragment->length = 0;
            fragment->s_port = stream->d_port;
            fragment->d_port = stream->s_port;
            fragment->seq_num = stream->seq_num;
            fragment->ack_num = stream->ack_num;
            fragment->tcp_flags = RTE_TCP_FIN_FLAG | RTE_TCP_ACK_FLAG;
            fragment->windows = TCP_INITIAL_WINDOW;
            fragment->hdrlen_off = 0x50;

            rte_ring_mp_enqueue(stream->send_buf, fragment);
            stream->status = TCP_STATUS_LAST_ACK;
            set_fd_frombitmap(fd);
        }else{
            struct tcp_table * table = get_tcp_instance();
            LL_REMOVE(stream, table->links);
            rte_free(stream);
        }
    }
    return 0;
}
//  udp


ssize_t m_recv_from(int fd, void * buf, size_t len, __attribute__((unused)) int flags, struct sockaddr * src_address, __attribute__((unused)) socklen_t * addr_len){
    printf("----------m_recv_from----------------->> \n");

    struct local_host * host = (struct local_host *)get_host_info_from_fd(fd);
    if(host == NULL){
        return 0;
    }

    struct udp_pkg * pkg = NULL;
    unsigned char * ptr = NULL;
    struct sockaddr_in * s_addr = (struct sockaddr_in *)src_address;
    int nb = -1;
    pthread_mutex_lock(&host->mtx);
    while((nb = rte_ring_mc_dequeue(host->recv_buf, (void **)&pkg)) < 0){
        pthread_cond_wait(&host->cond, &host->mtx);
    }
    pthread_mutex_unlock(&host->mtx);
    printf("-------m_recv_from---------unlock----------->> \n");
    s_addr->sin_port = pkg->s_port;
    rte_memcpy(&s_addr->sin_addr.s_addr, &pkg->s_ip, sizeof(uint32_t));
    if(len < pkg->length){
        rte_memcpy(buf, pkg->data, len);
        ptr = rte_malloc("unsigned char ptr ",pkg->length-len, 0);
        rte_memcpy(ptr, pkg->data + len, pkg->length - len);
        pkg->length -= len;
        rte_free(pkg->data);
        pkg->data = ptr;

        rte_ring_mp_enqueue(host->recv_buf, pkg);
        return len;
    } else{
        rte_memcpy(buf, pkg->data, pkg->length);
        rte_free(pkg->data);
        rte_free(pkg);
        return pkg->length;
    }
}


ssize_t m_send_to(int fd, const void * buf, size_t len, __attribute__((unused)) int flags, const struct sockaddr * dst_addr, __attribute__((unused)) socklen_t addr_len){
    printf("-------m_send_to-------------------->> \n");

    struct local_host * host = (struct local_host *)get_host_info_from_fd(fd);
    if(host == NULL){
        return  -1;
    }
    const struct sockaddr_in * d_addr = (const struct sockaddr_in *)dst_addr;
    struct udp_pkg * pkg = rte_malloc("m send to ", sizeof(struct udp_pkg), 0);
    if(pkg == NULL){
        return  -1;
    }
    pkg->d_ip = d_addr->sin_addr.s_addr;
    pkg->d_port = d_addr->sin_port;
    pkg->s_ip = host->local_ip;
    pkg->s_port = host->local_port;
    pkg->length = len;

    print_address("m send to src: ", d_addr->sin_addr.s_addr);
    printf("prot:  %d \n", d_addr->sin_port);
    pkg->data = rte_malloc("send to pkg data: ", len, 0);
    if(pkg->data == NULL){
        rte_free(pkg);
        return -1;
    }
    rte_memcpy(pkg->data, buf, len);
    rte_ring_mp_enqueue(host->send_buf, pkg);
    return  len;
}

// tcp
int m_listen(int sock_fd, __attribute__((unused)) int backlog){   // 仅修改状态
    struct tcp_stream * stream;
    if((stream = get_tcp_stream_from_fd(sock_fd)) == NULL){
        return -1;
    }
    if(stream->protocol == IPPROTO_TCP){
        stream->status = TCP_STATUS_LISTEN;
    }
    return 0;
}


int m_accept(int sock_fd, struct sockaddr * address, __attribute__((unused)) socklen_t * len){
    printf("----------m_accept--------------socket fd--%d->> \n", sock_fd);
    /**
     *  三次握手之后从全连接队列取出一个节点 分配fd
     * */
    struct tcp_stream * stream;
    if((stream = get_tcp_stream_from_fd(sock_fd)) == NULL){   //  找到的 listen fd 数据包 【半链接队列】
        return -1;
    }
    if (stream->protocol == IPPROTO_TCP) {
        struct tcp_stream * accept_stream = NULL;
        pthread_mutex_lock(&stream->mtx);
        while ((accept_stream = get_accept_tcb(stream->d_port)) == NULL){   // 从全连接队列中拿到一个节点
            pthread_cond_wait(&stream->cond, &stream->mtx);
        }
        pthread_mutex_unlock(&stream->mtx);
        printf("--------m_accept--------unlock----------->> \n");
        accept_stream->fd = get_fd_frombitmap();      // 分配fd
        struct sockaddr_in * saddr = (struct sockaddr_in *) address;
        saddr->sin_port = accept_stream->s_port;
        rte_memcpy(&saddr->sin_addr.s_addr, &accept_stream->s_ip, sizeof(uint32_t));
        return accept_stream->fd;
    }
    return -1;
}


ssize_t m_send(int sock_fd, const void * buf, size_t len, __attribute__((unused)) int flags){
    printf("--------m_send------------------->> \n");

    ssize_t length =0;
    struct tcp_stream * stream;
    if((stream = get_tcp_stream_from_fd(sock_fd)) == NULL){
        return -1;
    }
    if(stream->protocol == IPPROTO_TCP){
        struct tcp_fragment * fragment = rte_malloc("send malloc fragment", sizeof(struct tcp_fragment), 0);
        if (fragment == NULL) {
            return -2;
        }
        memset(fragment, 0, sizeof(struct tcp_fragment));
        printf("--------init--fragment----------------->> \n");
        fragment->d_port = stream->s_port;
        fragment->s_port = stream->d_port;
        fragment->ack_num = stream->ack_num;
        fragment->seq_num = stream->seq_num;
        fragment->tcp_flags = RTE_TCP_ACK_FLAG | RTE_TCP_PSH_FLAG;
        fragment->windows = TCP_INITIAL_WINDOW;
        fragment->hdrlen_off = 0x50;
        fragment->data = rte_malloc("send malloc fragment data", len + 1, 0);
        if(fragment->data == NULL){
            rte_free(fragment);
            return -1;
        }
        memset(fragment->data, 0, len + 1);
        rte_memcpy(fragment->data, buf, len);
        printf("--------fragment->data-----: %s--------len:%ld------>> \n", fragment->data, len);
        fragment->length = len;
        length = fragment->length;
        rte_ring_mp_enqueue(stream->send_buf, fragment);
    }
    return length;
}


ssize_t m_recv(int sock_fd, void * buf, size_t len, __attribute__((unused)) int flags){
    printf("--------m_recv------------------->> \n");

    ssize_t length = 0;
    struct tcp_stream * stream;
    if((stream = get_tcp_stream_from_fd(sock_fd)) == NULL){
        return -1;
    }
    if(stream->protocol == IPPROTO_TCP){
        struct tcp_fragment * fragment = NULL;
        int nb_rcv = 0;

        pthread_mutex_lock(&stream->mtx);
        while ((nb_rcv = rte_ring_mc_dequeue(stream->rcv_buf, (void **)&fragment)) < 0){  // 阻塞等待数据到来
            pthread_cond_wait(&stream->cond, &stream->mtx);
        }
        pthread_mutex_unlock(&stream->mtx);

        printf("--------nb_rcv--->> %d ---- \n", nb_rcv);
        printf("--------fragment--data->> %s ---- \n", fragment->data);
        printf("--------fragment--length->> %d ------s_port:  %d----d_port:  %d-- \n", fragment->length, fragment->s_port, fragment->d_port);

        if(fragment->length > len){  // 一次取不完，多次取
            rte_memcpy(buf, fragment->data, len);
            uint32_t  i = 0;
            for(i = 0; i < fragment->length - len; i ++){
                fragment->data[i] = fragment->data[len + i];
            }
            fragment->length = fragment->length - len;
            length = fragment->length;
            rte_ring_mp_enqueue(stream->rcv_buf, fragment);
        }else if(fragment->length == 0){
            rte_free(fragment);
            return 0;
        }else{
            rte_memcpy(buf, fragment->data, fragment->length);
            length = fragment->length;
            rte_free(fragment->data);
            fragment->data = NULL;
            rte_free(fragment);
        }
    }
    return length;
}